home *** CD-ROM | disk | FTP | other *** search
- /*
- File: DTS_SCSI_IO.c
-
-
-
-
-
- Unfortunately, no matter how long awaited, it's still not done. In fact, this
- isn't even a release- this is just an image of the code taken in the middle of
- development.
-
- THIS CODE DOES NOT WORK AS A WHOLE. MUCH OF IT IS BUGGY AND / OR INCOMPLETE.
- YOU WOULD HAVE TO BE ABSOLUTELY INSANE TO USE ANY OF THIS CODE IN YOUR
- PROJECT WITHOUT EXTENSIVE THOUGHT, DEBUGGING AND TESTING.
-
-
-
-
-
-
- Contains: Low-level SCSI input/output code to implement a SCSI Manager
- client, for the DTS SCSI Driver and its Formatting Application.
- All access to the SCSI Manager should go through these routines.
-
- This file contains these public routines:
-
- StuffSCSICommandBlock:
- Handy for initializing SCSI command descriptor blocks.
- The corresponding StuffTransferInstructionBlock is used
- within SCSIOp.
-
- SCSIOp: does the complete low-level SCSI sequence, with
- external control over timeout.
-
- SCSIPrime: does block I/O to/from the drive, using SCSIOp.
-
- This code is compiled in two flavors. If "-d INCLUDEVMCALLS" is
- defined on the compiler command line, SCSIOp includes calls
- to "hold" itself and its data structures during SCSI Manager calls.
- Normally, drivers don't need to worry about this because VM's
- Device Manager patches take care of holding the parameter block,
- data buffers, and the driver itself, but if we're including this
- code in an application, the calls are important.
-
- Note that because this code can be compiled to be part of the
- driver or of the application, no access to global storage is
- allowed; all calls here depend only on their arguments and do
- no allocation (other than on the stack for local variables).
-
- Written by: Mike Bell, Neil Day, Colleen Delgadillo, Tim Dierks, Dennis
- Hescox, Craig Prouse, Kent Sandvik, Bryan Stearns
-
- Copyright: © 1992 by Apple Computer, Inc., all rights reserved.
-
- Change History (most recent first):
-
- 3/14/92 BJS Combined code by Messrs Prouse & Sandvik
-
- To Do:
- */
-
- #include <Memory.h>
- #include <GestaltEqu.h>
- #include "DTS_SCSI_IO.h"
-
- #ifdef INCLUDEVMCALLS
- //
- // Is VM running?
- // If we get an error from Gestalt, VM can't possibly be around.
- //
- Boolean IsVirtualMemoryRunning() {
- long vmState;
- short error = Gestalt(gestaltVMAttr, &vmState); // test for VM
- return (error == noErr) ? vmState : false; // return false if Gestalt couldn't tell us.
- }
- #endif
-
- //
- // Fill in a SCSI Command block
- //
- void StuffSCSICommandBlock(SCSICommandBlock cmd, unsigned char a, unsigned char b,
- unsigned char c, unsigned char d, unsigned char e, unsigned char f) {
- // Just stuff the six bytes in order.
- cmd[0] = a; cmd[1] = b; cmd[2] = c; cmd[3] = d; cmd[4] = e; cmd[5] = f;
- }
-
- //
- // Fill in a Transfer Instruction Block. if the loopCount is nonzero, the TIB is
- // expected to be three instructions:
- //
- // @0: scInc buffer, blockSize
- // @1: scLoop @0, loopCount
- // @2: scStop 0, 0
- //
- // If loopCount is zero, the middle instruction is omitted (it's safe to pass in
- // a two-instruction TIB array in this case).
- //
- void StuffTransferInstructionBlock(TransferInstructionBlock tib, void *buffer, unsigned
- long blockSize, unsigned long loopCount) {
- register SCSIInstr *instr = tib;
-
- instr->scOpcode = scInc;
- instr->scParam1 = (unsigned long) buffer;
- (instr++)->scParam2 = blockSize;
- if (loopCount != 0) {
- instr->scOpcode = scLoop;
- instr->scParam1 = - sizeof(SCSIInstr);
- (instr++)->scParam2 = loopCount;
- }
- instr->scOpcode = scStop;
- instr->scParam1 = instr->scParam2 = 0;
- }
-
-
- //
- // This is an example of a subroutine to perform SCSI transactions. It works for
- // well-behaved targets which follow a predictable phase sequence, including most
- // Apple hard disks. It may require modifications for other device types such as
- // SCSI-2 devices or CD-ROM drives.
- //
- // Use of this subroutine requires some knowledge of the SCSI specification and the
- // SCSI Manager because the structure of a Command Descriptor Block and a Transfer
- // Instruction Block are not documented here.
- //
- // If virtual memory is running, everything needed to complete the SCSI transaction
- // must be held in physical memory, to prevent a page fault from occurring while the
- // SCSI bus is in use:
- // * For drivers, VM's patches to the Device Manager will take care of holding
- // the parameter block, the data buffer (ioBuffer[ioReqCount]), and 512 bytes
- // of stack space; the device control entry, DCtlStorage, and the driver code
- // itself are in the system heap, which is always held.
- //
- // * An application that makes SCSI Manager calls is responsible for holding all
- // of these structures itself. The code to do this is conditionally compiled
- // into this procedure: when this file is compiled for the application, the
- // compiler command line includes "-d INCLUDEVMCALLS" to trigger the
- // inclusion of this code, which holds the following structures:
- // * the SCSIOp code itself
- // * the SCSI Command Descriptor Block (assumed to be 16 bytes or less)
- // * the data buffer, if any
- // * the local SCSIOp stack area
- //
- // The strangest part of this holding operation is guessing the size of our stack
- // requirements: we can't know the amount of stack that the SCSI Manager will need
- // when we call it, nor can we find out from the compiler exactly how big our
- // stack frame is. So, we estimate both:
- #define kSCSIManagerStackEstimate 512
- #define kSCSIOpStackFrameSize 64
- //
- // Parameters (all input):
- //
- // cmd: Points at a SCSI Command Descriptor Block, filled in for this operation.
- // cmdSize: Size of the Command Descriptor Block (usually 6, 10, or 12 bytes).
- // targetSCSIID: SCSI bus ID of the requested target (0 - 6).
- // completionTimeout: timeout to pass in our call to SCSIComplete.
- // callback: idle procedure, used by Format to spin the cursor.
- // buffer: Points at the data buffer to be used, if any (NULL if none).
- // If buffer is NULL, the rest of these fields are ignored:
- // blockSize: The block size of the data to be transferred
- // loopCount: The number of blocks to be transferred, or 0 if the data is one block or less.
- // writing: true if we're writing, false if we're reading.
- // blind: true if we're doing a blind transfer, false if we're polling.
- //
- // Result:
- // 0: No error (GOOD)
- // < 0: Negated versions of SCSI Manager error result codes
- // > 0: SCSI status byte (e.g. CHECK CONDITION, BUSY, etc.)
-
- OSErr SCSIOp (SCSICommandBlock cmd, short cmdSize, SCSIAddress targetSCSIID, unsigned long
- completionTimeout, void *buffer, unsigned long blockSize,
- unsigned long loopCount, Boolean writing, Boolean blind) {
- SCSIInstr tib[3]; // The Transfer Instruction Block we'll use
- short status, message; // Results from SCSIComplete
- OSErr compErr, opErr; // Error codes along the way
- #ifdef INCLUDEVMCALLS //*** Document usage of INCLUDEVMCALLS
- short holdErr = noErr; // success in holding memory
- short heldCount; // number of accomplished HoldMemory calls
- unsigned long bufferHeld; // length of buffer to hold
- extern void NextFunction(); // a dummy function -- tells us where this one ends.
- short protectedStackSize; // How much we held for our stack
- char *protectedStackBase; // and where it starts
- Boolean vmOn = IsVirtualMemoryRunning(); // Is VM on?
-
- // *** for debugging safety: we don't allow any commands other than Test and
- // Inquiry for addresses other than 6
- if ((targetSCSIID != 6) && (cmd[0] != SCSICmd_TestUnitReady) && (cmd[0] != SCSICmd_Inquiry))
- debugstr("DANGER, WILL ROBINSON! Doing unsafe command to unsafe unit!"); //*** should be an assert.
-
- if (vmOn) { // Hold everything!
- // We need to be able to back out if we get an error during these "holds"
- heldCount = 0; // we'll keep track of
-
- // First, our code. It starts at SCSIOp and continues through the beginning
- // of the next function, which is a dummy function we've included just for
- // this purpose.
- holdErr = HoldMemory(SCSIOp, (unsigned long) NextFunction - (unsigned long) SCSIOp);
- if (holdErr == noErr) {
- heldCount = 1;
-
- // The SCSI command block is next. We assume that it's less than 16 bytes.
- holdErr = HoldMemory(cmd, 16);
- if (holdErr == noErr) {
- heldCount = 2;
-
- // The data buffer starts at "buffer" for at least "blockSize" bytes.
- // If "loopCount" is non-zero, we multiply the blocksize by it, too.
-
- bufferHeld = loopCount ? blockSize * loopCount : blockSize;
-
- holdErr = HoldMemory(buffer, bufferHeld);
- if (holdErr == noErr) {
- heldCount = 3;
-
- // Finally, the stack. We know we'll need some stack space to call
- // the SCSI Manager, and we'll need to protect our local variables.
- // So, we get the address of one of our local variables (they're on
- // the stack), subtract the amount of space we think the SCSI Manager
- // will need, then hold from there to above our locals by a fair margin.
- protectedStackBase = ((char *) &vmOn) - kSCSIManagerStackEstimate;
- protectedStackSize = kSCSIManagerStackEstimate + kSCSIOpStackFrameSize;
- holdErr = HoldMemory(protectedStackBase, protectedStackSize);
- if (holdErr == noErr) {
- heldCount = 4;
- }
- }
- }
- }
- }
-
- // Did all the holds succeed?
- if (holdErr == noErr)
- #endif
- {
- // If we're transferring data, make a Transfer Instruction Block, so
- // that the SCSI Manager will know where to get (or put) the data.
- // (Yes, StuffTransferInformationBlock is outside of the protected RAM.
- // This doesn't matter, because we haven't tied up the bus yet.)
- if (buffer != NULL)
- StuffTransferInstructionBlock(tib, buffer, blockSize, loopCount);
-
- // Now we'll do the SCSI Operation:
- // Arbitrate for control of the SCSI bus
- opErr = SCSIGet();
- if (opErr != noErr) {
- // We couldn't get the bus; skip out.
- return -opErr;
- }
-
- // We got the bus, so we'll have to call SCSIComplete at some point to release it.
- // Our next step is to select the specified target.
- opErr = SCSISelect(targetSCSIID);
- if (opErr == noErr) {
- // We've succeeded in finding our target -- send the command to it.
- opErr = SCSICmd(cmd, cmdSize);
-
- // If the caller expected data, we'll have gotten a pointer to a Transfer
- // Instruction Block; transfer the data, if any.
- if ((opErr == noErr) && (buffer != NULL)) {
- // This is a four-way selection, selecting between two pairs of
- // two operations, based on whether we're reading or writing and
- // whether we're doing a blind or polled transfer.
- opErr = writing ?
- (blind ? SCSIWBlind((Ptr)tib) : SCSIWrite((Ptr)tib)) :
- (blind ? SCSIRBlind((Ptr)tib) : SCSIRead((Ptr)tib));
- }
-
-
- // Complete the transaction and release the bus
- compErr = SCSIComplete(&status, &message, completionTimeout);
-
- // Must call SCSIComplete once SCSISelect succeeds, but
- // its result code should not mask any previous errors.
- if (opErr == noErr)
- opErr = compErr;
- }
- }
-
- #ifdef INCLUDEVMCALLS
- // We're done. Unhold the memory we held, in the reverse order that we
- // held it. Ignore any errors along the way. Note that we fall through
- // cases to unhold all the already held sections.
- switch (heldCount) {
- case 4:
- UnholdMemory(protectedStackBase, protectedStackSize);
- case 3:
- UnholdMemory(buffer, bufferHeld);
- case 2:
- UnholdMemory(cmd, 16);
- case 1:
- UnholdMemory(SCSIOp, (unsigned long) NextFunction - (unsigned long) SCSIOp);
- }
-
- // If we got an error while holding the memory, return it now.
- if (holdErr != noErr)
- return holdErr;
- #endif
-
- // Attempt to return the most valuable result possible. A SCSI Manager error is
- // negated to distinguish it from a positive SCSI status byte code, which may be
- // returned for a failed transaction even if the SCSI Manager is "successful."
- //
- // If there were no errors from the SCSI Manager, the result is the SCSI status
- // byte, otherwise it is the negation of the SCSI Manager error.
- //
- return opErr == noErr ? status : -opErr;
- }
- // A dummy function; we use it to find the end of the previous function, SCSIOp, when
- // holding its memory.
- void NextFunction() { }
-
- //
- // Transfer data over the SCSI bus. This is a wrapper for SCSIOp, above, that
- // knows that we're doing a read or write of data to the device. The data
- // buffer must be held in physical memory (if this call is being made by a disk
- // driver as a result of a _Read or _Write operation, VM will take care of this
- // for us).
- //
- // Parameters:
- // dataStart: Pointer to the start of the data to be transferred.
- // targetSCSIID: SCSI bus ID of the requested target.
- // targetUnit: Unit number of the requested target (usually 0).
- //
- // cmd: Pointer to a SCSICommandBlock, held in memory.
- // cmdSize: Size of the SCSICommandBlock (usually 6 bytes).
- // tib: Pointer to a TransferInstructionBlock, or NIL if none. Held in memory.
- // targetSCSIID: SCSI bus ID of the requested target (0 - 6).
- // startBlock: source or destination starting block number.
- // blockCountPtr: Pointer to the number of blocks to be transferred (updated on exit).
- // blockSize: Size of each block on this device.
- // writing: TRUE if we're writing, FALSE if we're reading, ignored if transferInstructionBlock
- // is NIL.
- // blind: TRUE if we're doing a blind transfer, false if we're polling. Ignored if
- // transferInstructionBlock = NIL.
- // completionTimeout: timeout to pass in our call to SCSIComplete.
- //
- OSErr SCSIPrime(void *dataStart, SCSIAddress targetSCSIID, short targetUnit, unsigned long startBlock,
- unsigned long *blockCountPtr, unsigned long blockSize, Boolean writing, Boolean blind,
- unsigned long completionTimeout) {
- SCSICommandBlock cmd;
- unsigned long reqBlocks, chunkBlocks, nextBlock;
- OSErr opErr = noErr;
- unsigned char *buffer = dataStart;
-
- // SCSI's low-level transfer operations are only capable of transferring a maximum of
- // 256 blocks, so we loop over each set of 256 blocks. (Note that in the actual SCSI
- // command descriptor block, a block count of zero means "256").
- reqBlocks = *blockCountPtr;
- nextBlock = startBlock;
- while (opErr == noErr && reqBlocks > 0) { // While we have blocks left...
- // Figure out how many blocks we'll be transferring this iteration.
- chunkBlocks = (reqBlocks <= 256) ? reqBlocks : 256;
-
- //
- // Set up the SCSI command block
- //
- // Byte Bits: 7 6 5 4 3 2 1 0
- // +-------------------------------+
- // 0 | Oper. code: Read=08, Write=0A |
- // 1 | Log. Unit | Block Addr (high) |
- // 2 | Logical Block Address (mid) |
- // 3 | Logical Block Address (low) |
- // 4 | Block count |
- // 5 | Control byte (always 0) |
- // +-------------------------------+
- //
- // (Thanks to Zaydoon)
-
- cmd[0] = writing ? SCSICmd_Write : SCSICmd_Read; // Command code
- cmd[1] = (targetUnit << 5) | (nextBlock >> 16); // Logical unit & bits 20-16 of address
- cmd[2] = nextBlock >> 8; // Bits 15-8 of address
- cmd[3] = nextBlock; // Bits 7-0 of address
- cmd[4] = chunkBlocks; // Block count (256 will be truncated to 0)
- cmd[5] = 0; // Not a linked command
-
- //
- // Finally, we do the transfer
- //
- opErr = SCSIOp(&cmd, 6 /* commandSize */, targetSCSIID, completionTimeout,
- buffer, blockSize,
- chunkBlocks, writing, blind);
-
- // If we were successful, update our running counters and buffer pointer.
- if (opErr == noErr) {
- nextBlock += chunkBlocks;
- buffer += (chunkBlocks * blockSize);
- reqBlocks -= chunkBlocks;
- }
- }
-
- *blockCountPtr -= reqBlocks; // indicate the actual number of blocks transferred
- return opErr;
- }
-
- #ifdef INCLUDEVMCALLS
- void LowLevelHoldEnd() { /* dummy routine - see HoldLowLevelCode(). */ }
- #endif